package com.rosiminc.pm.game;

/**
 * This class represents immutable, singleton tiles for PipesMania.
 * 
 * @author Shymon
 * 
 */
public class Tile {

	private static Tile[] factory;

	private int tileNum;
	private boolean right;
	private boolean up;
	private boolean left;
	private boolean down;
	private int numSides;

	/**
	 * This constructor creates a tile with a given tile code.
	 * 
	 * @param tileNum
	 *            The tile code
	 */
	private Tile(int tileNum) {
		this.tileNum = tileNum;
		this.left = tileNum / 8 == 1;
		this.up = tileNum % 8 / 4 == 1;
		this.right = tileNum % 4 / 2 == 1;
		this.down = tileNum % 2 == 1;

		numSides = 0;
		if (right)
			numSides++;
		if (left)
			numSides++;
		if (up)
			numSides++;
		if (down)
			numSides++;
	}

	/**
	 * This method returns the tile number.
	 * 
	 * @return the tile number
	 */
	public int getTileNumber() {
		return tileNum;
	}

	/**
	 * This method returns whether the tile is aiming left.
	 * 
	 * @return if aiming left
	 */
	public boolean isLeft() {
		return left;
	}

	/**
	 * This method returns whether the tile is aiming up.
	 * 
	 * @return if aiming up
	 */
	public boolean isUp() {
		return up;
	}

	/**
	 * This method returns whether the tile is aiming right.
	 * 
	 * @return if aiming right
	 */
	public boolean isRight() {
		return right;
	}

	/**
	 * This method returns whether the tile is aiming down.
	 * 
	 * @return if aiming down
	 */
	public boolean isDown() {
		return down;
	}

	/**
	 * This method returns the tile obtained by rotating the tile in a
	 * counter-clockwise motion.
	 * 
	 * @return The tile rotated left
	 */
	public Tile rotateLeft() {
		return factory[(tileNum * 2) - ((tileNum / 8) * 15)];
	}

	/**
	 * This method returns the tile obtained by rotating the tile in a clockwise
	 * motion.
	 * 
	 * @return The tile rotated right
	 */
	public Tile rotateRight() {
		return factory[(tileNum / 2) + 8 * (tileNum % 2)];
	}

	/**
	 * This method returns the number of connections allowed by the tile.
	 * 
	 * @return the number of sides
	 */
	public int getNumSides() {
		return this.numSides;
	}

	/**
	 * This static method gets from the tile factory a tile following the sent
	 * direction criteria.
	 * 
	 * @param left
	 *            If the tile aims left
	 * @param up
	 *            If the tile aims up
	 * @param right
	 *            If the tile aims right
	 * @param down
	 *            If the tile aims down
	 * @return the generated tile
	 */
	public static Tile getTile(boolean left, boolean up, boolean right,
			boolean down) {
		return getTile((left ? 8 : 0) + (up ? 4 : 0) + (right ? 2 : 0)
				+ (down ? 1 : 0));
	}

	/**
	 * This method gets from the tile factory the tile containing the given tile
	 * number.
	 * 
	 * @param tileNum
	 *            The tile number
	 * @return the generated tile
	 * @throws IllegalArgumentException
	 *             if the tile number is not inclusively between 0 and 15.
	 */
	public static Tile getTile(int tileNum) {
		if (tileNum < 0 || tileNum > 15)
			throw new IllegalArgumentException(
					"Tile number out of usable range");
		if (factory == null)
			createFactory();
		return factory[tileNum];
	}

	/**
	 * This method creates the factory of tiles.
	 */
	private static void createFactory() {
		factory = new Tile[16];
		for (int i = 0; i < 16; i++)
			factory[i] = new Tile(i);
	}
}
